#ifndef __QUERY__
#define __QUERY__

#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <memory>
#include <unordered_map>
#include <set>
#include <algorithm>
#include <iterator>

using std::string;
using std::cout;
using std::endl;
using std::ifstream;
using std::shared_ptr;
using std::unordered_map;
using std::set;
using std::ostream;
using std::istringstream;
using std::set_union;
using std::set_intersection;
using std::inserter;


class TextQuery;
class Query;
class WordQuery;
class NotQuery;
class QueryResult;
class Query_base;

class QueryResult {
public:
    friend class TextQuery;
    friend class NotQuery;
    friend class OrQuery;
    friend class AndQuery;
	friend ostream &print(ostream &os, const QueryResult &);
    friend ostream &operator<<(ostream&os, const QueryResult &);
private:
	QueryResult(const string &qword, 
                const shared_ptr<set<int>> &lineNum,
                shared_ptr<unordered_map<int, string>>file);
    string qword;	
    shared_ptr<set<int>>lineNum;
    shared_ptr<unordered_map<int, string>>file;
};

class TextQuery{
public:
    friend class WordQuery;
    friend class NotQuery;
    friend class OrQuery;
    friend class AndQuery;
    TextQuery(ifstream &fd);
    QueryResult query(const string &word)const;

private:
    shared_ptr<unordered_map<int, string>>file;
    shared_ptr<unordered_map<string, set<int>>>words;
};

class Query_base {
protected:
    friend class Query;
    virtual QueryResult eval(const TextQuery &) = 0;
    virtual string rep()const = 0;
public:
    /* virtual ~Query_base() = 0; */
};

class WordQuery : public Query_base {
    friend class Query;
    WordQuery(const string &query_word);
    QueryResult eval(const TextQuery &) override;
    string rep()const override;

    string query_word;
public:
    /* ~WordQuery(); */
};


class Query {
public:
    Query(shared_ptr<Query_base>pbase);
    Query(string qWord);	

    QueryResult eval(const TextQuery &);
    string rep()const ;
    friend ostream &operator<<(ostream &os, const Query &);
    friend Query operator~(const Query &);
    friend Query operator|(const Query &, const Query &);
    friend Query operator&(const Query &, const Query &);

private:
    shared_ptr<Query_base>pbase;

};

class NotQuery:public Query_base{
    friend class Query;
    friend Query operator~(const Query &);

    NotQuery(const Query &);
    QueryResult eval(const TextQuery &) override;
    string rep()const override;

    Query query;

};

class BinaryQuery:public Query_base{
protected:
    BinaryQuery(const Query&, const Query&, const string&);
    string rep()const override;

    Query lsh;
    Query rsh;
    string opSym;
};

class OrQuery:public BinaryQuery{
    friend class Query;
    friend Query operator|(const Query &, const Query &);
    
    OrQuery(const Query&, const Query&, const string&);
    QueryResult eval(const TextQuery &) override;

};

class AndQuery:public BinaryQuery{
    friend class Query;
    friend Query operator&(const Query &, const Query &);

    AndQuery(const Query&, const Query&, const string&);
    QueryResult eval(const TextQuery &) override;
};

#endif // !__QUERY__

